import Vue,{PropOptions,PropType,VNode} from 'vue';
import Modal from '../modal';
import ArrowScroll from '../arrow-scroll';
import Input from '../input';
import Icon from '../icon';
import Button from '../button';
import TextEllipsis from '../text-ellipsis';
import SelectArea from '../select-area';
import Tree from '../tree';
import Checkbox from '../checkbox';
import Radio from '../radio';
import MyKeepAlive from '../my-keep-alive';
import {QSplitter} from 'quasar';
import {PlainObject,SizeObj} from '../../../../types';
import {ComputedObj,NodeScope,RawObj} from '../../js/pc/mixin/treeMixin';
import {MySet} from '../../../util/polyfill';
//region 定义接口
/**
 * 人员搜索过滤
 * @param {string} string				过滤文本
 * @param {MySet<PlainObject>} members	所有人员
 * @return {MySet<PlainObject>}			过滤后的人员
 */
export interface MemberSearchFilter{
	(search:string,members:MySet<PlainObject>):MySet<PlainObject>
}
/**
 * 过滤人员方法
 * @param {RawObj|null} filterObj		过滤信息
 * @param {MySet<PlainObject>} members	所有人员
 * @return {MySet<PlainObject>}			过滤后的人员
 */
export interface CategoryFilterMethod{
	(filterObj:RawObj|null,members:MySet<PlainObject>):MySet<PlainObject>
}
/**
 * 类别标签
 * @property {string} label			类别名
 * @property {boolean} tickable		可否勾选
 * @property {Array<RawObj>} data	树数据
 * @property {CategoryFilterMethod} filter	过滤人员方法
 */
export interface CategoryTab{
	label:string,
	tickable:boolean,
	data:Array<RawObj>,
	filter:CategoryFilterMethod,
}
/**
 * 类别树信息
 * @property {MySet<RawObj>} expanded	展开信息
 * @property {RawObj|null} selected		选中信息
 * @property {MySet<RawObj>} ticked		勾选信息
 */
export interface TabCache{
	expanded:MySet<RawObj>,
	selected:RawObj|null,
	ticked:MySet<RawObj>,
}
//endregion
/**
 * 选人组件, 支持10w以上的人员和上万的类别列表
 * @author 唐国雄
 * @date 2021/8/26
 * 属性----------------------
 * v-model					显示状态, boolean
 * :width					宽度, 支持带单位和不带单位, number | string
 * :height					高度, 支持带单位和不带单位, number | string
 * :tabs					所有类别树, Array<CategoryTab>
 * :tree-obj-key			树唯一键, string
 * :tree-label-key			类别树显示字段, string
 * :children-key			类别树子节点字段, string
 * :members					所有人员, MySet<PlainObject>
 * :member-search-filter	人员搜索过滤, MemberSearchFilter
 * :single					单选, boolean
 * 插槽----------------------
 * #tools					额外的工具栏
 * #item-content			选择信息内容, {item:PlainObject, index:number}
 * #tab-content				类别树节点内容
 * 事件----------------------
 * @confirm		确认事件
 * @close		关闭事件
 * @show		开始显示
 * @shown		显示动画完毕
 * @hide		开始隐藏
 * @hidden		隐藏动画完毕
 */
export default Vue.extend({
	name:'ChooseMember',
	inheritAttrs:false,
	props:{
		//显示状态
		value:{
			type:Boolean,
			required:true,
		},
		//宽度, 支持带单位和不带单位
		width:{
			type:[Number,String],
			default:900,
		},
		//高度, 支持带单位和不带单位
		height:{
			type:[Number,String],
			default:700,
		},
		//所有类别树
		tabs:{
			type:Array,
			required:true,
		} as PropOptions<Array<CategoryTab>>,
		//树唯一键
		treeObjKey:{
			type:String,
			required:true,
		},
		//类别树显示字段
		treeLabelKey:{
			type:String,
			default:'label',
		},
		//类别树子节点字段
		childrenKey:String,
		//所有人员
		members:MySet as PropType<MySet<PlainObject>>,
		//人员搜索过滤
		memberSearchFilter:Function as PropType<MemberSearchFilter>,
		//单选
		single:Boolean,
	},
	data(){
		const tabCaches:{[key:string]:TabCache}={};
		const tabs=this.tabs;
		for(let i=0;i<tabs.length;++i){
			const {label,data}=tabs[i];
			tabCaches[label]={
				selected:null,
				expanded:new MySet<RawObj>(data),
				ticked:new MySet<RawObj>(),
			};
		}
		return {
			//搜索值
			search:'',
			//真正搜索的值
			searchConfirm:'',
			//当前类别
			curTabIndex:0,
			//纵向分隔比例
			splitterValue:50,
			//提示附着元素
			appendTo:null,
			//弹窗宽度
			bodyWidth:0,
			//分类过滤数据
			filterTabData:new MySet<PlainObject>(),
			//类别树的, 选中, 勾选, 展开的缓存信息
			tabCaches,
			//所有勾选的人员
			checkedMember:new MySet<PlainObject>(),
			//所有过滤数据中被选中的
			allFilterDataSelect:new MySet<PlainObject>(),
			//所有勾选中被选中的
			allCheckedSelect:new MySet<PlainObject>(),
		};
	},
	computed:{
		classes():string{
			return 'lms-ChooseMember';
		},
		//每个部分宽度
		partWidth():number{
			return Math.floor((this.bodyWidth-42)/2);
		},
		//当前类别
		curTab():CategoryTab{
			return this.tabs[this.curTabIndex];
		},
		//当前类别的缓存信息
		curCache():TabCache{
			return this.tabCaches[this.curTab.label]||{
				selected:null,
				expanded:new MySet<RawObj>(),
				ticked:new MySet<RawObj>(),
			};
		},
		//人员过滤数据
		filterMembers():MySet<PlainObject>{
			if(this.members){
				let members=this.members;
				if(this.curCache.selected){
					members=this.curTab.filter(this.curCache.selected,members);
				}
				if(this.searchConfirm){
					members=this.memberSearchFilter(this.searchConfirm,members);
				}
				return members;
			}
			return new MySet();
		},
		//所有过滤数据
		allFilterData():MySet<PlainObject>{
			const allFilterData=new MySet(this.filterTabData);
			allFilterData.addAll(this.filterMembers);
			return allFilterData;
		},
		//所有勾选
		allChecked():MySet<PlainObject>{
			const tabCaches=this.tabCaches;
			const tabs=this.tabs;
			const allChecked=new MySet<PlainObject>();
			for(let i=0;i<tabs.length;++i){
				const tabCache=tabCaches[tabs[i].label];
				if(tabCache){
					allChecked.addAll(tabCache.ticked);
				}
			}
			allChecked.addAll(this.checkedMember);
			return allChecked;
		},
		//勾选的概览信息
		selectInfo():string{
			let msg='';
			if(this.checkedMember.size){
				msg=', '+this.checkedMember.size+'人';
			}
			const tabCaches=this.tabCaches;
			const tabs=this.tabs;
			for(let i=0;i<tabs.length;++i){
				const label=tabs[i].label;
				const tabCache=tabCaches[label];
				if(tabCache&&tabCache.ticked.size){
					msg+=', '+tabCache.ticked.size+label;
				}
			}
			return msg&&msg.substring(2);
		}
	},
	watch:{
		//勾选的值已经变化
		allChecked(){
			const allChecked=this.allChecked;
			this.allCheckedSelect.filterSelf(function(ele){
				return allChecked.has(ele);
			});
			//因为这里select就必须是勾选的数据(allChecked)
			this.allFilterDataSelect=this.allFilterData.filter(function(ele){
				return allChecked.has(ele);
			});
		},
		//过滤数据变化
		allFilterData(){
			const allChecked=this.allChecked;
			//因为这里select就必须是勾选的数据(allChecked)
			this.allFilterDataSelect=this.allFilterData.filter(function(ele){
				return allChecked.has(ele);
			});
		},
		//类别树变化
		tabs(){
			const tabCaches:{[key:string]:TabCache}={};
			const tabs=this.tabs;
			for(let i=0;i<tabs.length;++i){
				const {label,data}=tabs[i];
				tabCaches[label]={
					selected:null,
					expanded:new MySet<RawObj>(data),
					ticked:new MySet<RawObj>(),
				};
			}
			this.tabCaches=tabCaches;
		},
	},
	methods:{
		//搜索
		searchFn(){
			this.searchConfirm=this.search;
			const searchConfirm=this.searchConfirm;
			const filterTabData=this.filterTabData;
			filterTabData.clear();
			if(searchConfirm&&this.curTab.tickable){
				const treeLabelKey=this.treeLabelKey;
				// @ts-ignore
				const metas:Array<ComputedObj>=this.$refs.tree.getMetas();
				let startPos=0;
				let minTab=-1;
				if(this.curCache.selected){
					// @ts-ignore
					const selected:ComputedObj=this.$refs.tree.getMeta(this.curCache.selected[this.treeObjKey]);
					minTab=selected.tab;
					startPos=metas.indexOf(selected);
				}
				for(let i=startPos;i<metas.length;++i){
					const {rawObj,tickable,tab}=metas[i];
					if(tab<=minTab&&i!==startPos){
						break;
					}
					if(tickable&&rawObj[treeLabelKey].indexOf(searchConfirm)!==-1){
						filterTabData.add(rawObj);
					}
				}
			}
		},
		//清空搜索
		clearSearch(){
			this.searchConfirm=this.search='';
			this.filterTabData.clear();
		},
		//通过框选勾选
		checkedBySelect(allSelect:MySet<PlainObject>){
			const members=this.members;
			const selectMembers=[];
			let selectOther;
			if(members){
				selectOther=[];
				for(const item of allSelect){
					if(members.has(item)){
						selectMembers.push(item);
					}else{
						selectOther.push(item);
					}
				}
			}else{
				selectOther=allSelect;
			}
			//改变已勾选的人员
			const checkedMember=this.checkedMember;
			checkedMember.deleteAll(this.filterMembers);
			checkedMember.addAll(selectMembers);
			//改变当前已勾选的树
			const ticked=this.curCache.ticked;
			ticked.deleteAll(this.filterTabData);
			ticked.addAll(selectOther);
		},
		singleCheck(item:PlainObject){
			this.checkedMember.clear();
			for(const {ticked} of Object.values(this.tabCaches)){
				ticked.clear();
			}
			if(this.members&&this.members.has(item)){
				this.checkedMember.add(item);
			}else{
				this.curCache.ticked.add(item);
			}
		},
	},
	render(h):VNode{
		const itemContent=this.$scopedSlots['item-content'];
		const tools=this.$scopedSlots.tools;
		const tabContent=this.$scopedSlots['tab-content'];
		return h(Modal,{
			ref:'Modal',
			staticClass:this.classes,
			props:{
				value:this.value,
				fullWidth:true,
				title:'请选择',
				width:this.width,
				height:this.height,
				...this.$attrs,
			},
			on:{
				input:(val:boolean) => {
					this.$emit('input',val);
					//关闭弹窗后的操作
					if(!val){
						this.allCheckedSelect.clear();
						this.clearSearch();
					}
				},
				show:() => {
					this.curTabIndex=0;
					this.$emit('show');
					this.$nextTick(function(){
						// @ts-ignore
						this.appendTo=this.$refs.Modal.$el;
					});
				},
				shown:() => {
					this.$emit('shown');
				},
				hide:() => {
					this.$emit('hide');
				},
				hidden:() => {
					this.$emit('hidden');
				},
				confirm:() => {
					this.$emit('confirm');
				},
				close:() => {
					this.$emit('close');
				},
				resize:({width}:SizeObj) => {
					if(this.bodyWidth!==width){
						this.bodyWidth=width;
					}
				},
			},
			scopedSlots:{
				default:({height}) => h('div',{
					staticClass:'lms-ChooseMember_content',
					staticStyle:{
						height:height-(this.$baseui_theme==='fk'?20:0)+'px'
					},
				},[
					h('div',{
						staticClass:'lms-ChooseMember_head',
					},[
						h(ArrowScroll,{
							staticClass:'lms-ChooseMember_labels',
						},this.tabs.map((tab,index) => {
							return h('div',{
								staticClass:'lms-ChooseMember_label'+
									(this.curTabIndex===index?' lms--active':''),
								on:{
									click:() => {
										this.curTabIndex=index;
										this.clearSearch();
									}
								},
							},tab.label);
						})),
						h('div',{
							staticClass:'lms-ChooseMember_tools',
						},[
							tools&&tools(undefined),
							h(Input,{
								staticClass:'lms-ChooseMember_search',
								props:{
									value:this.search,
									placeholder:'请输入',
								},
								on:{
									input:(val:string) => {
										this.search=val;
									},
								},
								nativeOn:{
									'!keydown':({key}:KeyboardEvent) => {
										if(key==='Enter'){
											this.searchFn();
										}
									},
								},
								scopedSlots:{
									append:() => h(Icon,{
										staticClass:'lms-ChooseMember_searchIcon',
										props:{
											name:'search',
										},
										on:{
											click:this.searchFn,
										}
									})
								},
							})
						])
					]),
					h('div',{
						staticClass:'lms-ChooseMember_body',
					},[
						h(QSplitter,{
							staticClass:'lms-ChooseMember_left',
							props:{
								value:this.splitterValue,
								horizontal:true,
								separatorClass:'lms-ChooseMember_hideSeparator',
							},
							on:{
								input:(val:number) => {
									this.splitterValue=val;
								}
							},
							scopedSlots:{
								before:() => h(MyKeepAlive,[
									h(Tree,{
										ref:'tree',
										staticClass:'lms-ChooseMember_tree',
										key:this.curTabIndex,
										props:{
											objKey:this.treeObjKey,
											data:this.curTab.data,
											virtualScroll:true,
											labelKey:this.treeLabelKey,
											childrenKey:this.childrenKey,
											tickStrategy:this.curTab.tickable?'strict':'none',
											noLeafTicking:!this.curTab.tickable,
											selectable:!!this.members,
											selectToggle:true,
											tickedOnClick:!this.members,
											selected:this.curCache.selected,
											treeExpanded:this.curCache.expanded,
											treeTicked:this.curCache.ticked,
											minWidth:this.partWidth,
											beforeTicked:this.single
												?({meta:{rawObj}}:NodeScope) => {
													this.singleCheck(rawObj);
													return true;
												}
												:null,
										},
										on:{
											'update:selected':(val:RawObj) => {
												this.curCache.selected=val;
												this.clearSearch();
											}
										},
										scopedSlots:{
											content:tabContent
										}
									})
								]),
								after:() => h('div',{
									staticClass:'lms-ChooseMember_leftBottom'
								},[
									h('div',{
										staticClass:'lms-ChooseMember_infoBar',
									},[
										h('div',{
											staticClass:'lms-ChooseMember_info',
										},this.members&&!this.searchConfirm
											?'人员:'
											:'过滤信息:'
										),
										h('div',{
											staticClass:'lms-ChooseMember_text',
										}),
										this.single
											?null
											:h(Button,{
												staticClass:'lms-ChooseMember_but',
												props:{
													type:'default',
													outline:this.$baseui_theme==='red',
												},
												on:{
													click:() => {
														this.allFilterDataSelect=new MySet(this.allFilterData);
														this.checkedBySelect(this.allFilterDataSelect);
													}
												}
											},'全选'),
									]),
									h(SelectArea,{
										staticClass:'lms-ChooseMember_list',
										props:{
											lineHeight:38,
											data:this.allFilterData,
											value:this.allFilterDataSelect,
											disable:this.single,
										},
										on:{
											input:this.checkedBySelect,
										},
										scopedSlots:{
											default:({allSelect,index,item}:{allSelect:MySet<PlainObject>,index:number,item:PlainObject}) => h('div',{
												staticClass:'lms-ChooseMember_item'+
													(allSelect.has(item)?' lms--active':''),
												key:index,
												on:{
													click:() => {
														this.singleCheck(item);
													},
												}
											},[
												this.single
													?h(Radio,{
														props:{
															value:allSelect.has(item),
															val:true,
														},
														on:{
															input:() => {
																this.singleCheck(item);
															}
														},
														nativeOn:{
															mousedown:(e:Event) => {
																e.stopPropagation();
															}
														}
													})
													:h(Checkbox,{
														props:{
															value:allSelect.has(item),
														},
														on:{
															input:(val:boolean) => {
																if(this.members&&this.members.has(item)){
																	if(val){
																		this.checkedMember.add(item);
																	}else{
																		this.checkedMember.delete(item);
																	}
																}else{
																	if(val){
																		this.curCache.ticked.add(item);
																	}else{
																		this.curCache.ticked.delete(item);
																	}
																}
															}
														},
														nativeOn:{
															mousedown:(e:Event) => {
																e.stopPropagation();
															}
														}
													}),
												itemContent&&itemContent({index,item})
											])
										},
									}),
								]),
							}
						}),
						h('div',{
							staticClass:'lms-ChooseMember_right',
						},[
							h('div',{
								staticClass:'lms-ChooseMember_infoBar',
							},[
								h('div',{
									staticClass:'lms-ChooseMember_info',
								},'已选:'),
								h(TextEllipsis,{
									staticClass:'lms-ChooseMember_text',
									props:{
										tip:true,
										content:this.selectInfo,
									},
									attrs:{
										appendTo:this.appendTo,
									}
								}),
								this.allCheckedSelect.size>1
									?h(Button,{
										staticClass:'lms-ChooseMember_but',
										props:{
											type:'default',
											outline:this.$baseui_theme==='red',
										},
										on:{
											click:() => {
												const tabCachesArr=Object.values(this.tabCaches);
												const checkedMember=this.checkedMember;
												const allCheckedSelect=this.allCheckedSelect;
												for(const item of allCheckedSelect){
													if(checkedMember.has(item)){
														checkedMember.delete(item);
														continue;
													}
													for(let i=0;i<tabCachesArr.length;++i){
														tabCachesArr[i].ticked.delete(item);
													}
												}
											}
										}
									},'删除')
									:null,
								h(Button,{
									staticClass:'lms-ChooseMember_but',
									props:{
										type:'default',
										outline:this.$baseui_theme==='red',
									},
									on:{
										click:() => {
											this.allCheckedSelect=new MySet(this.allChecked);
										}
									}
								},'全选'),
							]),
							h(SelectArea,{
								staticClass:'lms-ChooseMember_list',
								props:{
									lineHeight:38,
									data:this.allChecked,
									value:this.allCheckedSelect,
								},
								on:{
									input:(allSelect:MySet<PlainObject>) => {
										this.allCheckedSelect=allSelect;
									},
								},
								scopedSlots:{
									default:({allSelect,index,item}:{allSelect:MySet<PlainObject>,index:number,item:PlainObject}) => h('div',{
										staticClass:'lms-ChooseMember_item'+
											(allSelect.has(item)?' lms--active':''),
										key:index,
									},[
										itemContent&&itemContent({index,item}),
										h(Icon,{
											staticClass:'lms-ChooseMember_close',
											props:{
												name:'close',
											},
											on:{
												click:() => {
													const tabCachesArr=Object.values(this.tabCaches);
													if(this.members&&this.members.has(item)){
														this.checkedMember.delete(item);
													}else{
														for(let i=0;i<tabCachesArr.length;++i){
															tabCachesArr[i].ticked.delete(item);
														}
													}
												},
												mousedown:(e:Event) => {
													e.stopPropagation();
												},
											}
										}),
									])
								},
							}),
						])
					]),
				]),
			}
		});
	}
});
